package com.infoland.modules.basic;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;

import javax.servlet.http.Cookie;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.infoland.app.Constants;
import com.jfinal.core.ActionException;
import com.jfinal.core.Controller;
import com.jfinal.kit.LogKit;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Table;
import com.jfinal.plugin.activerecord.TableMapping;
import com.jfinal.render.RenderManager;

public class BaseController<M extends Model<M>> extends Controller {
	private static final RenderManager renderManager = RenderManager.me();
	private Class<M> modelClass;

	public JSONArray getJSONArray() {
		JSONArray array;
		try {
			array = JSONArray.parseArray(getAttrForStr("body"));
		} catch (JSONException e) {
			throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
					"Can not parse the parameter");
		}
		return array;
	}

	public JSONArray getJSONArray(String param) {
		JSONObject jsonObject;
		try {
			jsonObject = JSONObject.parseObject(getAttrForStr("body"));
		} catch (JSONException e) {
			throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
					"Can not parse the parameter");
		}
		if (jsonObject.isEmpty()) {
			return null;
		}
		return jsonObject.getJSONArray(param);
	}

	public Integer[] getJSONValuesToInt(String param) {
		JSONArray jsonArray = getJSONArray(param);
		return jsonArray.toArray(new Integer[] {});
	}

	public Object[] getJSONValues(String param) {
		JSONArray jsonArray = getJSONArray(param);
		return jsonArray.toArray();
	}

	public JSONObject getJSONObject() {
		JSONObject jsonObject;
		try {
			jsonObject = JSONObject.parseObject(getAttrForStr("body"));
		} catch (JSONException e) {
			throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
					"Can not parse the parameter");
		}
		if (jsonObject == null) {
			return new JSONObject();
		}
		return jsonObject;
	}

	public JSONObject getJSONObject(String name) {
		JSONObject jsonObject;
		try {
			jsonObject = JSONObject.parseObject(getAttrForStr("body"));
		} catch (JSONException e) {
			throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
					"Can not parse the parameter");
		}
		if (jsonObject == null) {
			return new JSONObject();
		}
		return jsonObject.getJSONObject(name);
	}

	@SuppressWarnings("unchecked")
	public <T> T getJSONModel(String name, Class<? extends Model<?>> clazz) {
		JSONObject jsonObject = getJSONObject(name);
		Object object = null;
		try {
			Map<String, Object> attrs = new HashMap<>();
			object = clazz.newInstance();
			Table table = TableMapping.me().getTable(clazz);
			for (Map.Entry<String, Class<?>> entry : table.getColumnTypeMap().entrySet()) {
				if (jsonObject.get(entry.getKey()) != null) {
					if (entry.getValue().getName().contains("java.lang.Long")) {
						attrs.put(entry.getKey(), Long.parseLong(jsonObject.get(entry.getKey()).toString()));
					} else {
						attrs.put(entry.getKey(), jsonObject.get(entry.getKey()));
					}
				}
			}
			Method setAttrsMethod = clazz.getMethod("_setAttrs", Map.class);
			setAttrsMethod.invoke(object, attrs);
		} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException
				| IllegalArgumentException | InvocationTargetException e) {
			LogKit.info(e.getMessage());
			throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
					"Can not parse the parameter!");
		}
		return (T) object;
	}

	@SuppressWarnings("unchecked")
	public <T> T getJSONModel(Class<? extends Model<?>> clazz) {
		JSONObject jsonObject = getJSONObject();
		Object object = null;
		try {
			Map<String, Object> attrs = new HashMap<>();
			object = clazz.newInstance();
			Table table = TableMapping.me().getTable(clazz);
			for (Map.Entry<String, Class<?>> entry : table.getColumnTypeMap().entrySet()) {
				if (jsonObject.get(entry.getKey()) != null) {
					if (entry.getValue().getName().contains("java.lang.Long")) {
						attrs.put(entry.getKey(), Long.parseLong(jsonObject.get(entry.getKey()).toString()));
					} else {
						attrs.put(entry.getKey(), jsonObject.get(entry.getKey()));
					}
				}
			}
			Method setAttrsMethod = clazz.getMethod("_setAttrs", Map.class);
			setAttrsMethod.invoke(object, attrs);
		} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException
				| IllegalArgumentException | InvocationTargetException e) {
			LogKit.info(e.getMessage());
			throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
					"Can not parse the parameter!");
		}
		return (T) object;
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> getJSONModelList(Class<? extends Model<?>> clazz) {
		JSONArray jsonArray = getJSONArray();
		List<T> list = new ArrayList<>();
		for (Object iObject : jsonArray) {
			JSONObject jsonObject = (JSONObject) iObject;
			Object object = null;
			try {
				Map<String, Object> attrs = new HashMap<>();
				object = clazz.newInstance();
				Table table = TableMapping.me().getTable(clazz);
				for (Map.Entry<String, Class<?>> entry : table.getColumnTypeMap().entrySet()) {
					if (jsonObject.get(entry.getKey()) != null) {
						if (entry.getValue().getName().contains("java.lang.Long")) {
							attrs.put(entry.getKey(), Long.valueOf(jsonObject.get(entry.getKey()).toString()));
						} else {
							attrs.put(entry.getKey(), jsonObject.get(entry.getKey()));
						}
					}
				}
				Method setAttrsMethod = clazz.getMethod("_setAttrs", Map.class);
				setAttrsMethod.invoke(object, attrs);
			} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException
					| IllegalArgumentException | InvocationTargetException e) {
				LogKit.info(e.getMessage());
				throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
						"Can not parse the parameter!");
			}
			list.add((T) object);
		}
		return list;
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> getJSONModelList(String name, Class<? extends Model<?>> clazz) {
		JSONArray jsonArray = getJSONArray(name);
		List<T> list = new ArrayList<>();
		for (Object iObject : jsonArray) {
			JSONObject jsonObject = (JSONObject) iObject;
			Object object = null;
			try {
				Map<String, Object> attrs = new HashMap<>();
				object = clazz.newInstance();
				Table table = TableMapping.me().getTable(clazz);
				for (Map.Entry<String, Class<?>> entry : table.getColumnTypeMap().entrySet()) {
					if (jsonObject.get(entry.getKey()) != null) {
						if (entry.getValue().getName().contains("java.lang.Long")) {
							attrs.put(entry.getKey(), Long.valueOf(jsonObject.get(entry.getKey()).toString()));
						} else {
							attrs.put(entry.getKey(), jsonObject.get(entry.getKey()));
						}
					}
				}
				Method setAttrsMethod = clazz.getMethod("_setAttrs", Map.class);
				setAttrsMethod.invoke(object, attrs);
			} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException
					| IllegalArgumentException | InvocationTargetException e) {
				LogKit.info(e.getMessage());
				throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
						"Can not parse the parameter!");
			}
			list.add((T) object);
		}
		return list;
	}

	public String getJSONPara(String name) {
		JSONObject object = JSONObject.parseObject(getAttrForStr("body"));
		if ((object != null) && (!(object.isEmpty()))) {
			return object.getString(name);
		}
		return null;
	}

	public String getJSONPara(String name, String defaultValue) {
		String result = getJSONPara(name);
		return ((StrKit.isBlank(result)) ? defaultValue : result);
	}

	public Integer getJSONParaToInt(String name) {
		String result = getJSONPara(name);
		return toInt(result, null);
	}

	public Integer getJSONParaToInt(String name, Integer defaultValue) {
		String result = getJSONPara(name);
		return toInt(result, defaultValue);
	}

	public Long getJSONParaToLong(String name) {
		String result = getJSONPara(name);
		return toLong(result, null);
	}

	public Long getJSONParaToLong(String name, Long defaultValue) {
		String result = getJSONPara(name);
		return toLong(result, defaultValue);
	}

	public Boolean getJSONParaToBoolean(String name) {
		String result = getJSONPara(name);
		return toBoolean(result, null);
	}

	public Boolean getJSONParaToBoolean(String name, Boolean defaultValue) {
		String result = getJSONPara(name);
		return toBoolean(result, defaultValue);
	}

	public Date getJSONParaToDate(String name) {
		String result = getJSONPara(name);
		return toDate(result, null);
	}

	public Date getJSONParaToDate(String name, Date defaultValue) {
		String result = getJSONPara(name);
		return toDate(result, defaultValue);
	}

	private Date toDate(String value, Date defaultValue) {
		try {
			if (StrKit.isBlank(value))
				return defaultValue;
			return new SimpleDateFormat("yyyy-MM-dd").parse(value.trim());
		} catch (Exception e) {
			throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
					"Can not parse the parameter \"" + value + "\" to Date value.");
		}
	}

	private Integer toInt(String value, Integer defaultValue) {
		try {
			if (StrKit.isBlank(value))
				return defaultValue;
			value = value.trim();
			if ((value.startsWith("N")) || (value.startsWith("n")))
				return Integer.valueOf(-Integer.parseInt(value.substring(1)));
			return Integer.valueOf(Integer.parseInt(value));
		} catch (Exception e) {
			throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
					"Can not parse the parameter \"" + value + "\" to Integer value.");
		}
	}

	private Long toLong(String value, Long defaultValue) {
		try {
			if (StrKit.isBlank(value))
				return defaultValue;
			value = value.trim();
			if ((value.startsWith("N")) || (value.startsWith("n")))
				return Long.valueOf(-Long.parseLong(value.substring(1)));
			return Long.valueOf(Long.parseLong(value));
		} catch (Exception e) {
			throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
					"Can not parse the parameter \"" + value + "\" to Long value.");
		}
	}

	private Boolean toBoolean(String value, Boolean defaultValue) {
		if (StrKit.isBlank(value))
			return defaultValue;
		value = value.trim().toLowerCase();
		if (("1".equals(value)) || ("true".equals(value)))
			return Boolean.TRUE;
		if (("0".equals(value)) || ("false".equals(value)))
			return Boolean.FALSE;
		throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),
				"Can not parse the parameter \"" + value + "\" to Boolean value.");
	}

	public BaseController() {
		this.modelClass = getClazz();
	}

	@SuppressWarnings("unchecked")
	public Class<M> getClazz() {
		Type t = super.getClass().getGenericSuperclass();
		Type[] params = ((ParameterizedType) t).getActualTypeArguments();
		return ((Class<M>) params[0]);
	}

	public List<M> getModelList(String modelName, Integer num) {
		return getModelList(getModelClass(), modelName, num);
	}

	public <T> List<T> getModelList(Class<T> modelClass, String modelName, Integer num) {
		List<T> list = new ArrayList<>();
		for (int i = 0; i < num.intValue(); ++i) {
			list.add(getModel(modelClass, modelName + "[" + i + "]"));
		}
		return list;
	}

	public List<String> getParalList(String paraName, Integer num) {
		List<String> list = new ArrayList<>();
		for (int i = 0; i < num.intValue(); ++i) {
			list.add(getPara(paraName + "[" + i + "]"));
		}
		return list;
	}

	public Class<M> getModelClass() {
		return this.modelClass;
	}

	public void setModelClass(Class<M> modelClass) {
		this.modelClass = modelClass;
	}
	
	public void shareSession(String username) {
		Cookie cookie = new Cookie(Constants.SESSION_NAME, username);
		cookie.setPath("/");
		cookie.setMaxAge(-1);
//		UserInfo userInfo = null;
//		if(userInfo==null){
//			UserInfo ui = new UserInfo();
//			ui.setCreateDate(new Date());
//			ui.setUserName(username);;
//			ui.save();
//		}
//		int se = 1000*60*60*24;
//		RedisKit.getCache().setex(userInfo.getId(),se, userInfo);
//		setAttr(Constants.SESSION_NAME, username);
		setCookie(cookie);
	}
	
	public <T> List<T> getModelList(Class<T> modelClass,String modelName){
        Pattern p = Pattern.compile(modelName + "\\[\\d\\].[a-zA-z0-9]+");
        Map<String, String[]> parasMap = getRequest().getParameterMap();
        String paraKey;
        Set<String> modelPrefix = new HashSet<String>();
        for (Entry<String, String[]> e : parasMap.entrySet()) {
            paraKey = e.getKey();
            if(p.matcher(paraKey).find()){
                modelPrefix.add(paraKey.split("\\.")[0]);
            }
        }
        List<T> resultList = new ArrayList<T>();
        for (String modelName2 : modelPrefix) {
            resultList.add(getModel(modelClass,modelName2));
        }
        return resultList;
    }
}